<?php

/**
 * SvcPHP
 *
 * Copyright (C) 2012 João Francisco Biondo Trinca
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    SvcPHP
 * @author     João Francisco Biondo Trinca <wolfulus@gmail.com>
 * @copyright  2012 João Francisco Biondo Trinca <wolfulus@gmail.com>
 * @license    http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link       http://code.google.com/p/svcphp/
 *
 */

require_once('Svc/Template/Renderer.php');
require_once('Svc/Template/Data.php');

/**
 * Walk function
 *
 * @param string $val
 * @param mixed $index
 */
function Svn_Template_Walk(&$val, $index)
{
    $val = ucfirst($val);
}

;

/**
 * 5.3 functions
 */
if (!function_exists('lcfirst'))
{
    function lcfirst($string)
    {
        return substr_replace($string, strtolower(substr($string, 0, 1)), 0, 1);
    }
}

/**
 * Implements the template class.
 */
class Svc_Template
{
    /**
     * Stores the path to the view scripts directory.
     *
     * @var string
     */
    private $_viewDir = "";

    /**
     * Stores the path to the cache directory.
     *
     * @var string
     */
    private $_cacheDir = "";

    /**
     * Stores the cache timeout.
     *
     * @var int
     */
    private $_cacheTimeout = 3600;

    /**
     * Stores the default file extension.
     *
     * @var string
     */
    private $_fileExtension = 'html';

    /**
     * Stores the user-defined variables.
     *
     * @var mixed
     */
    private $_data = null;

    /**
     * Stores the registered plugins.
     *
     * @var mixed
     */
    private $_plugins = array();

    /**
     * Creates a template class instance.
     *
     * @param array $options
     *
     * @return Svc_Template
     */
    public function __construct($options)
    {
        foreach ($options as $key => $value)
        {
            $values = explode("_", $key);
            array_walk($values, 'Svn_Template_Walk');

            $member = "_" . lcfirst(@implode("", $values));
            if (isset($this->$member))
            {
                $this->$member = $value;
            }
        }

        $this->_data = new Svc_Template_Data();
        $this->_plugins = array();

        $this->fixPaths();
    }

    /**
     * Fixes some variable suffixes.
     *
     * @return void
     */
    private function fixPaths()
    {
        $final = substr($this->_viewDir, 0, -1);
        if (!($final == '\\' || $final == '/'))
        {
            $this->_viewDir .= DIRECTORY_SEPARATOR;
        }

        if (!is_dir($this->_viewDir))
        {
            @mkdir($this->_viewDir, 0777, true);
        }
        else
        {
            if (!is_readable($this->_viewDir))
            {
                throw new Svc_Exception(Svc_Exception::TEMPLATE_VIEWDIR_ACCESS_ERROR, $this->_viewDir);
            }
        }

        $final = substr($this->_cacheDir, 0, -1);
        if (!($final == '\\' || $final == '/'))
        {
            $this->_cacheDir .= DIRECTORY_SEPARATOR;
        }

        if (!is_dir($this->_cacheDir))
        {
            @mkdir($this->_cacheDir, 0755, true);
        }

        if (!is_writable($this->_cacheDir))
        {
            throw new Svc_Exception(Svc_Exception::TEMPLATE_CACHEDIR_ACCESS_ERROR, $this->_cacheDir);
        }
    }

    /**
     * Gets the path to a cache file.
     *
     * @param mixed $file
     */
    private function getCacheFilePath($file)
    {
        $start = substr($file, 0, 1);
        if ($start == "/" || $start == "\\")
        {
            $file = substr($file, 1);
        }

        return $this->_cacheDir . $file;
    }

    /**
     * Gets the path to a view file.
     *
     * @param mixed $file
     */
    private function getViewFilePath($file)
    {
        $start = substr($file, 0, 1);
        if ($start == "/" || $start == "\\")
        {
            $file = substr($file, 1);
        }

        return $this->_viewDir . $file;
    }

    /**
     * Sets a template variable value.
     *
     * @param string $variable
     * @param mixed $value
     */
    public function assign($variable, $value = null)
    {
        if (is_array($variable))
        {
            foreach ($variable as $key => $val)
            {
                $this->_data->assign($key, $val);
            }

            return;
        }
        $this->_data->assign($variable, $value);
    }

    /**
     * Clears all variables from the template.
     *
     * @return void
     */
    public function clear()
    {
        $this->_data->clear();
    }

    /**
     * Registers a template plugin.
     *
     * @param Svc_Template_Plugin $plugin
     */
    public function registerPlugin($plugin)
    {
        $plugin->register($this->_data);
        $this->_plugins[$plugin->id()] = $plugin;
    }

    /**
     * Registers a template plugin.
     *
     * @param string $id
     *
     * @return Svc_Template_Plugin
     */
    public function & getPlugin($id)
    {
        return $this->_plugins[$id];
    }

    /**
     * Renders a template using the current variables.
     *
     * @param $template
     *
     * @throws Svc_Exception
     * @internal param mixed $file
     * @return string
     */
    public function render($template)
    {
        $path = $this->getViewFilePath($template . "." . $this->_fileExtension);
        if (!file_exists($path))
        {
            throw new Svc_Exception(Svc_Exception::TEMPLATE_NOT_FOUND, $template);
        }

        if (!is_readable($path))
        {
            throw new Svc_Exception(Svc_Exception::TEMPLATE_ACCESS_ERROR, $template);
        }

        $contents = file_get_contents($path);
        $hash = sha1($contents);

        $compile = false;
        $cachePath = $this->getCacheFilePath($hash . ".php");
        if (file_exists($cachePath))
        {
            // clearstatcache(false, $cachePath);
            if (time() - filemtime($cachePath) >= $this->_cacheTimeout)
            {
                unlink($cachePath);
                $compile = true;
            }
        }
        else
        {
            $compile = true;
        }

        if ($compile)
        {
            $contents = $this->compileTemplate($contents, $template . "." . $this->_fileExtension);
            file_put_contents($cachePath, $contents);
        }

        return $this->_data->render($cachePath);
    }

    /**
     * Compiles a razor template into PHP code.
     *
     * @param mixed $contents
     * @param $file
     *
     * @return string
     */
    private function compileTemplate($contents, $file)
    {
        $renderer = new Svc_Template_Renderer($contents, $file);

        return $renderer->generate();
    }

}
